﻿using System;
using System.Collections.Generic;
using System.Linq;
using LeoK.Common;
using MySql.Data.MySqlClient;
using System.Threading.Tasks;
using LeoK.DCI.Entity;

namespace LeoK.DCI.Bll
{
    public class DCIManager
    {
        #region 数据库操作

        public static async Task<DCI_DayLine> GetDayLine(DateTime date)
        {
            DCI_DayLine dayLine;

            using (MySqlDB db = new MySqlDB())
            {
                dayLine = await db.QueryObj<DCI_DayLine>("SELECT * FROM DCI_DayLine WHERE Date=@Date", new List<MySqlParameter>(){new MySqlParameter("Date", date)});
                DCI_DayLine preDayLine = await db.QueryObj<DCI_DayLine>("SELECT * FROM DCI_DayLine WHERE Date<@Date ORDER BY Date DESC", new List<MySqlParameter>() { new MySqlParameter("Date", date) });

                if (dayLine == null)
                {
                    dayLine = new DCI_DayLine();
                    if (preDayLine == null)
                    {
                        dayLine.Open = 80;
                    }
                    else
                    {
                        dayLine.Open = preDayLine.Close;
                    }

                    dayLine.Date = date;
                    dayLine.Close = dayLine.Open;
                    dayLine.High = dayLine.Open;
                    dayLine.Low = dayLine.Open;
                    dayLine.PreClose = dayLine.Open;
                    // 初始化分时节点
                    for (int i = 0; i < 48; i++)
                    {
                        DayPoint point = new DayPoint();
                        point.Time = new DateTime(1990, 1, 1).AddMinutes(i*30);
                        point.Value = dayLine.Open;

                        dayLine.DayPoints.Add(point);
                    }
                    ToDayPointStr(dayLine);
                    dayLine.ID = await db.InsertObj(dayLine);
                }
                else
                {
                    if (preDayLine == null)
                    {
                        dayLine.PreClose = 80;
                    }
                    else
                    {
                        dayLine.PreClose = preDayLine.Close;
                    }
                    ToDayPoints(dayLine);
                }
            }

            return dayLine;
        }

        public static async Task<List<DCI_DayLine>> GetDayLines()
        {
            List<DCI_DayLine> result;

            using (MySqlDB db = new MySqlDB())
            {
                result = await db.QueryObjs<DCI_DayLine>("SELECT * FROM DCI_DayLine ORDER BY Date");
                
                if (result.Count > 0)
                {
                    result[0].PreClose = result[0].Open;
                    
                    // 处理开盘价
                    for (int i = 1; i < result.Count; i++)
                    {
                        result[i].PreClose = result[i - 1].Close;
                        
                        ToDayPoints(result[i]);
                    }
                }

            }

            return result;
        }

        public static async void Save(DCI_DayLine dayLine)
        {
            using (MySqlDB db = new MySqlDB(true))
            {
                try
                {
                    List<DCI_DayLine> afterDayLines = await db.QueryObjs<DCI_DayLine>("SELECT * FROM DCI_DayLine WHERE Date>@Date", new List<MySqlParameter>() {new MySqlParameter("Date", dayLine.Date)});
                    if (afterDayLines.Count > 0)
                    {
                        DCI_DayLine savedDayLine = await db.QueryObj<DCI_DayLine>("SELECT * FROM DCI_DayLine WHERE ID=" + dayLine.ID);
                        double diff = dayLine.DayPoints[47].Value - savedDayLine.Close;

                        if (diff > 0)
                        {
                            foreach (DCI_DayLine afterDayLine in afterDayLines)
                            {
                                ToDayPoints(afterDayLine);
                                foreach (DayPoint point in afterDayLine.DayPoints)
                                {
                                    point.Value += diff;
                                }
                                ToDayPointStr(afterDayLine);

                                afterDayLine.Open += diff;
                                afterDayLine.Close += diff;
                                afterDayLine.High += diff;
                                afterDayLine.Low += diff;
                            }

                            db.UpdateObjs(afterDayLines);
                        }
                    }
                    
                    dayLine.Open = dayLine.DayPoints[0].Value;
                    dayLine.Close = dayLine.DayPoints[47].Value;
                    dayLine.High = dayLine.DayPoints.Max(p => p.Value);
                    dayLine.Low = dayLine.DayPoints.Min(p => p.Value);
                    ToDayPointStr(dayLine);
                    db.UpdateObj(dayLine);
                    db.Commit();
                }
                catch (Exception)
                {
                    db.Rollback();
                    throw;
                }
            }
        }

        private static void ToDayPointStr(DCI_DayLine dayLine)
        {
            dayLine.DayPointStr = "";
            foreach (DayPoint point in dayLine.DayPoints)
            {
                dayLine.DayPointStr += Math.Round(point.Value, 2) + ",";
            }
            dayLine.DayPointStr = dayLine.DayPointStr.TrimEnd(',');
        }

        private static void ToDayPoints(DCI_DayLine dayLine)
        {
            dayLine.DayPoints.Clear();
            string[] points = dayLine.DayPointStr.Split(',');
            for (int j = 0; j < 48; j++)
            {
                DayPoint dayPoint = new DayPoint();
                dayPoint.Time = new DateTime(1990, 1, 1).AddMinutes(30 * j);
                dayPoint.Value = double.Parse(points[j]);

                dayLine.DayPoints.Add(dayPoint);
            }
        }

        #endregion

        #region MA

        public static List<MAData> GetMA(List<DCI_DayLine> dayLines, int d)
        {
            List<MAData> result = new List<MAData>();
            List<DCI_DayLine> orderedDayLines = dayLines.OrderBy(p => p.Date).ToList();

            for (int i = 0; i < orderedDayLines.Count; i++)
            {
                int index = i - d < 0 ? 0 : i - d + 1;
                int count = i - d < 0 ? i + 1 : d;
                result.Add(new MAData { Date = orderedDayLines[i].Date, Value = orderedDayLines.GetRange(index, count).Average(p => p.Close) });
            }

            return result;
        }

        public static List<MAData> GetMA(List<DayPoint> points)
        {
            List<DayPoint> orderedPoints = points.OrderBy(p => p.Time).ToList();

            return orderedPoints.Select((t, i) => new MAData {Date = t.Time, Value = orderedPoints.GetRange(0, i + 1).Average(p => p.Value)}).ToList();
        }

        #endregion

        #region MACD

        public static List<MACDData> GetMACDData(List<DCI_DayLine> dayLines)
        {
            List<MACDData> result = new List<MACDData>();
            MACDData preMACDData = null;

            foreach (DCI_DayLine dayLine in dayLines)
            {
                MACDData macdData = new MACDData();
                macdData.Date = dayLine.Date;

                if (preMACDData == null)
                {
                    macdData.EMA12 = dayLine.Close*2/13;
                    macdData.EMA26 = dayLine.Close*2/27;
                    macdData.DIFF = macdData.EMA12 - macdData.EMA26;
                    macdData.DEA = macdData.DIFF*2/10;
                    macdData.BAR = (macdData.DIFF - macdData.DEA)*2;
                }
                else
                {
                    macdData.EMA12 = preMACDData.EMA12*11/13 + dayLine.Close * 2 / 13;
                    macdData.EMA26 = preMACDData.EMA26*25/27 + dayLine.Close * 2 / 27;
                    macdData.DIFF = macdData.EMA12 - macdData.EMA26;
                    macdData.DEA = preMACDData.DEA*8/10 + macdData.DIFF * 2 / 10;
                    macdData.BAR = (macdData.DIFF - macdData.DEA) * 2;
                }

                preMACDData = macdData;

                result.Add(macdData);
            }

            return result;
        }

        #endregion
    }

    public class MAData
    {
        public DateTime Date { get; set; }
        public double Value { get; set; }
    }

    public class MACDData
    {
        public DateTime Date { get; set; }
        public double EMA12 { get; set; }
        public double EMA26 { get; set; }
        public double DIFF { get; set; }
        public double DEA { get; set; }
        public double BAR { get; set; }
    }
}
